package com.gao.r_hash.f._1;

import org.junit.Test;

import java.lang.reflect.Method;
import java.util.concurrent.ThreadLocalRandom;

/**
 * @author gao
 * @time 2022/06/27 21:32:36
 * ConcurrentHashMap 1.8 中的线程探针哈希
 */
public class App {
    /**
     * 使用ThreadLocalRandom.getProbe()得到线程的探针哈希值。
     * 由于getProbe方法是包级访问权限，所以我们不能直接调用该方法，
     * 只能使用反射机制来强制调用该方法。
     */
    @Test
    public void test() throws Exception {
        Class<ThreadLocalRandom> cls = ThreadLocalRandom.class;
        Method method = cls.getDeclaredMethod("getProbe");
        method.setAccessible(true);
        // getProbe方法是静态的，所以不用通过实例调用
        // 且该方法总是返回0
        Object r = method.invoke(null);
        System.out.println(r);
        r = method.invoke(null);
        System.out.println(r);
        r = method.invoke(null);
        System.out.println(r);
    }

    /**
     * ThreadLocalRandom 类的 localInit 方法完成初始化工作。
     * 在调用了 localInit 方法之后，getProbe 方法的值就不再是
     * 0了。此后每次调用 localInit 都会改变 getProbe 的值
     */
    @Test
    public void test2() throws Exception {
        Class<ThreadLocalRandom> cls = ThreadLocalRandom.class;
        Method localInitMethod = cls.getDeclaredMethod("localInit");
        Method getProbeMethod = cls.getDeclaredMethod("getProbe");
        localInitMethod.setAccessible(true);
        getProbeMethod.setAccessible(true);

        System.out.println("======================第一次调用localInit==========================");
        // 调用localInit方法以初始化getProbe的值
        localInitMethod.invoke(null);
        // 调用getProbe
        Object r = getProbeMethod.invoke(null);
        System.out.println(r);
        r = getProbeMethod.invoke(null);
        System.out.println(r);
        r = getProbeMethod.invoke(null);
        System.out.println(r);

        System.out.println("======================第二次调用localInit==========================");
        // 再次调用localInit方法
        localInitMethod.invoke(null);
        r = getProbeMethod.invoke(null);
        System.out.println(r);
        r = getProbeMethod.invoke(null);
        System.out.println(r);
        r = getProbeMethod.invoke(null);
        System.out.println(r);
    }

    /**
     * 也可以通过 ThreadLocalRandom 类的 advanceProbe 方法更改当前线程 threadLocalRandomProbe 的值
     */
    @Test
    public void test3() throws Exception {
        Class<ThreadLocalRandom> cls = ThreadLocalRandom.class;
        Method localInitMethod = cls.getDeclaredMethod("localInit");
        Method advanceProbeMethod = cls.getDeclaredMethod("advanceProbe", int.class);
        Method getProbeMethod = cls.getDeclaredMethod("getProbe");
        localInitMethod.setAccessible(true);
        advanceProbeMethod.setAccessible(true);
        getProbeMethod.setAccessible(true);

        System.out.println("======================调用localInit==========================");
        // 调用localInit方法以初始化getProbe的值
        localInitMethod.invoke(null);
        // 调用getProbe
        Object r = getProbeMethod.invoke(null);
        System.out.println(r);
        r = getProbeMethod.invoke(null);
        System.out.println(r);
        r = getProbeMethod.invoke(null);
        System.out.println(r);

        System.out.println("======================第一次调用advanceProbe==========================");
        // 调用advanceProbe方法改变getProbe的值，传入之前的getProbe的值
        advanceProbeMethod.invoke(null, r);
        r = getProbeMethod.invoke(null);
        System.out.println(r);
        r = getProbeMethod.invoke(null);
        System.out.println(r);
        r = getProbeMethod.invoke(null);
        System.out.println(r);

        System.out.println("======================第二次调用advanceProbe==========================");
        // 调用advanceProbe方法改变getProbe的值，传入之前的getProbe的值
        advanceProbeMethod.invoke(null, r);
        r = getProbeMethod.invoke(null);
        System.out.println(r);
        r = getProbeMethod.invoke(null);
        System.out.println(r);
        r = getProbeMethod.invoke(null);
        System.out.println(r);
    }

}
